<?php
/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
define('OWLXML_VERSION', '1.0');

define('BASE_SCHEMA_URI',"http://xml.owl.org/schema/");

class Owl_Db {


    var $show_error = true;
    var $last_error = '';
    var $last_query = '';
    var $document_path ='';
    var $schema_path ='';
    var $schema_uri = '';
    var $document = null;
    var $xpath = null;    
    var $ready = false;
    
    var $tables = array (
                            'course' => "courses.xml",
                            'product' => "products.xml",
                            'option' => "options.xml",
                            'note' => "notes.xml",
                            'personal' => "personals.xml",
                            'theme' => "themes.xml",
                            'page' => "pages.xml",
                            'ulang' => "ulangs.xml"
                        );
    var $schemas = array (
                            'course' => "course.xsd",
                            'product' => "product.xsd",
                            'option' => "option.xsd",
                            'note' => "note.xsd",
                            'personal' => "personal.xsd",
                            'theme' => "theme.xsd",
                            'page' => "page.xsd",
                            'ulang' => "ulang.xsd"
                        );
    

    function __construct($table, $file = false, $schema = false) {
        register_shutdown_function(array(&$this, "__destruct"));

        if (class_exists('Owl_Error'))
            $this->last_error = new Owl_Error();
            
        if ($file)
            $this->document_path = $file;
        else
            $this->document_path = DATA_PATH.$this->tables[$table];

        if ( empty ($table) || !file_exists($this->document_path) ){
            $this->bail(sprintf(__("
                    <h1>Database file or table is not exists</h1>
                    <p>Can't load database file <code>%s</code>.</p>"),$this->document_path));
                return;
        }
        else {            
            $this->document = new DOMDocument("1.0","UTF-8");
            $this->document->preserveWhiteSpace = false;
            $this->document->formatOutput = true;
            if ($schema)
                $this->schema_path = $schema;
            else
                $this->schema_path = SCHEMA_PATH.$this->schemas[$table];
            if (!file_exists($this->schema_path)){
                 $this->bail(sprintf(__("
                    <h1>Schema file is not exists</h1>
                    <p>Can't load schema file <code>%s</code>.</p>"),$this->schema_path));
                return;
            }            
            $this->schema_uri = BASE_SCHEMA_URI.$table;
            $this->load();
           
            //libxml_use_internal_errors(true);            
        }
    }
    
    function __destruct() {
	return true;
    }

    function Owl_Db($table, $file = false, $schema = false) {
       $this->__construct($table, $file, $schema);       
    }
    
    protected function fill_object($node){
        if ($this->is_field_node($node)) {
            return $node->nodeValue;
        }
        $object  = Array();
        if ($node->hasChildNodes())
            foreach ( $node->childNodes as $child){
                if ($this->is_field_node($child))
                    $object[$child->nodeName] = $child->nodeValue;
                else {
                    foreach ($child->childNodes as $node)
                        $object[$child->nodeName][] = $this->fill_object($node);
                }
            }
        return $object;
    }

    protected function is_field_node($node) {
        return ($node->hasChildNodes() && count($node->childNodes) == 1 && $node->childNodes->item(0)->nodeType == XML_TEXT_NODE);
    }

    protected function query($query){
        $this->load();
        $this->last_query = $query;
        if ($this->ready === false){
            $this->last_error->add("500", __("Connection is not ready"));
            return array();
        } else {
            $query=preg_replace('/(\/|\[)([a-zA-Z])/','$1x:$2',$query);
            if (get_magic_quotes_runtime()) {
                $query = stripcslashes($query);
            }
            return $this->xpath->query($query);
        }        
    }

    function get_row($query, $unique = true) {
        
        $this->load();
        $node = $this->query($query);
        if ($node->length == 0){
            $this->last_error->add("500", __("Empty row"));
            return false;
        } else if ($node->length > 1 && $unique == true) {
            $this->last_error->add("500", __("Multi row return"));
            return false;
        } else {
            $node = $node->item(0);
            return $this->fill_object($node);
        }
    }

    protected function get_results($query) {
        $this->load();
        $node_list = $this->query($query);
        $result = array();
        foreach ($node_list as $node) {
            $result[] = $this->fill_object($node);
        }
        return $result;
    }

    protected function fill_node($root,$data){
            if ( is_array($data) ){
                $node = $this->document->createElementNS($this->schema_uri,$root);
                if ( array_key_exists(0, $data) ) {
                    $keygen = substr($root,0,-1);
                     foreach($data as $value){
                        $node->appendChild($this->fill_node($keygen, $value));
                     }
                } else {
                     foreach($data as $key => $value){
                        $node->appendChild($this->fill_node($key, $value));
                     }
                }
                return $node;
            } else {
                return $this->document->createElementNS($this->schema_uri,$root,$data);
            }        
    }
    
    protected function insert($parent_path,$object,$data){
        $this->load();
        $parent = $this->query($parent_path);
        if ($parent->length != 1){
            $this->last_error->add("500", __("Parent node not found"));
            return false;
        } else {
            $parent = $parent->item(0);
        }

        $new_node = $this->fill_node($object, $data);
        $parent->appendChild($new_node);       
        if (!$this->validate()){
            $parent->removeChild($new_node);
            return false;
        }

        if (!$this->document->save($this->document_path)){
            $this->last_error->add("500", sprintf(__("Can not save file %s"), $this->document_path));
            return false;
        } else {
            return true;
        }

    }
    
    protected function update($old_node_path,$object,$data){
        $this->load();
        $old_node = $this->query($old_node_path);        
        if ($old_node->length != 1){
            $this->last_error->add("500", __("Node not found"));
            return false;
        } else {
            $old_node = $old_node->item(0);
        }
        $parent_node = $old_node->parentNode;
        $new_node = $this->fill_node($object, $data);        
        $parent_node->replaceChild($new_node, $old_node);
        
        if (!$this->validate()){
            $parent_node->replaceChild($old_node, $new_node);
            return false;
        }
        if (!$this->document->save($this->document_path)){
            $this->last_error->add("500", sprintf(__("Can not save file %s"), $this->document_path));
            return false;
        } else {
            return true;
        }
    }

    protected function delete($node_path){
        $this->load();
        $node = $this->query($node_path);
        if ($node->length != 1){
            $this->last_error->add("500", __("Node not found"));
            return false;
        } else {
            $node = $node->item(0);
        }
        $parent_node = $node->parentNode;
        $parent_node->removeChild($node);

        if (!$this->validate()){
            $parent_node->appendChild($node);
            return false;
        }
        if (!$this->document->save($this->document_path)){
            $this->last_error->add("500", sprintf(__("Can not save file %s"), $this->document_path));
            return false;
        } else {
            return true;
        }
    }

    protected function insert_nor_update($parent_path,$old_node_path,$object,$data){
        $old_node = $this->query($old_node_path);
        if ($old_node->length > 1){
            $this->last_error->add("500", __("Multi node"));
            return false;
        } else if ($old_node->length == 0){
           $this->insert($parent_path, $object, $data);
        } else {
           $this->update($parent_path, $old_node_path, $object, $data);
        }
    }

    function print_error(){
        $this->last_error->print_friendly_error();
    }

    function get_error() {
        return $this->last_error->get_friendly_error();
    }
    
    function timer_start() {
	$mtime = microtime();
	$mtime = explode(' ', $mtime);
	$this->time_start = $mtime[1] + $mtime[0];
	return true;
    }

    function timer_stop() {
	$mtime = microtime();
	$mtime = explode(' ', $mtime);
	$time_end = $mtime[1] + $mtime[0];
	$time_total = $time_end - $this->time_start;
	return $time_total;
    }

    function bail($message) {
		if ( !$this->show_errors ) {
			if ( class_exists('Owl_Error') )
				$this->last_error->add('500', $message);
			else
				$this->last_error = $message;
			return false;
		}
		die($message);		
    }
    private function validate(){
        if (!$this->document->schemaValidate($this->schema_path)){
            $this->last_error->add('500', __('Schema is not validate'));
            return false;
        } else {
            return true;
        }

    }
    private function load(){
       if ( !$this->document->load($this->document_path) ){
                $this->bail(sprintf("
                    <h1>Error establishing a database connection</h1>
                    <p>Can't load database file <code>%s</code>.</p>
                    <ul>
                            <li>Are you sure you do not delete or move the file?</li>
                            <li>Are you sure apache(www-data) has permission to read the file?</li>
                            <li>Are you sure this code is not modified?</li>
                    </ul>
                    <p>If you still need help you can always contact me at the
                    <a mailto='shinbe8x@gmail.com'>Support Person</a>.</p>",$this->document_path));
                $this->ready = false;
                return;
            }

            if ( !$this->document->schemaValidate($this->schema_path)){
                $this->bail(sprintf("
                    <h1>Error establishing a database connection</h1>
                    <p> Can't validate shema <code>%s</code>.</p>
                    <ul>
                            <li>Are you sure you do not delete or move the schema file ?</li>
                            <li>Are you sure you not modify database file(xml) ?</li>
                            <li>Are you sure apache(www-data) has permission to read schema file and database file?</li>
                    </ul>
                    <p>If you still need help you can always contact me at the
                    <a mailto='shinbe8x@gmail.com'>Support Person</a>.</p>",$this->schema_path));
                $this->ready = false;
                return;
            }
            $this->xpath = new DOMXPath($this->document);
            $this->xpath->registerNamespace('x',$this->schema_uri);
            $this->ready = true;            
    }
}
